<?php 
namespace town\base;

class App
{
    private static $obj = null;

    public $route = null;

    /**
     * $path config file path
     * $debug app debug
     */
    public function __construct($debug = false)
    {
        if ($debug) {
            define('APP_DEBUG', true);
        } else {
            define('APP_DEBUG', false);
        }
        self::$obj = $this;
    }


    /**
     * 运行app
     */
    public function run()
    {
        $this->start();
        $this->handle();
        $this->end();
    }


    /**
     * 启动,初始化
     */
    private function start()
    {
        // -1.session_start()
        session_start();

        // 0.注册错误处理
        Log::singleton();

        // 1.解析路由,引入路由
        include APP_PATH.DS.'conf'.DS.'route.php';
    	$route = Route::dispatch();
        if ($route == false) {
            abort(404);
        }
        $this->route = $route;

        // 2.处理数据产生全局仓库
        Request::initData($route['args']);

        // 3.初始化过滤器
        $filters = include APP_PATH.DS.'conf'.DS.'filter.php';
        Request::initFilter($this->_filterMatch($filters, $route));
        Request::addFilter($route['filter']);
    }


    /**
     * 发送回执信息
     */
    private function end()
    {
        response()->send();
    }


    /**
     * 通过路由,初始化控制器
     * @param  array $route 路由信息
     * @return boolean
     */
    private function handle()
    {
        $route = $this->route;

        // 先过滤器
        $ret = Request::doFilter();
        
        if ($ret === false) {
            return false;
        }

        // 处理函数
        $action = $route['controller'];
        if (is_callable($action)) {
            $action(Request::singleton());

        } elseif (is_string($action)) {
            list($class, $method) = explode('@', $action);
            
            // 1.实例化对象,传入参数
            $params= [];
            $class = 'town\\controller\\'.$class.'Controller';
            $ref_class = new \ReflectionClass($class);
            if ($ref_class->hasMethod('__construct')) {
                $params = $this->_parseParams($class, '__construct');
            }
            $object = $ref_class->newInstanceArgs($params);

            // 2.分析方法参数,调用路由函数
            $params = $this->_parseParams($class, $method);
            $result = call_user_func_array([$object, $method], $params);
        }
    }


    /**
     * 处理当前路由匹配的过滤器
     * ['user@login;game@info;image@*', false]
     * @return [type] [description]
     */
    private function _filterMatch($filters, $route)
    {
        $filter_list = [];
        // 'user@login;game@info;image@*' | '*'
        $isMatch = function ($routes, &$action) {
            if ($routes == '*') {
                return true;
            }

            list($class, $method) = explode('@', $action);
            $list = explode(';', $routes);
            foreach ($list as $t_action) {
                list($t_class, $t_method) = explode('@', $t_action);
                if (($t_class == $class && $t_method == '*')
                    || ($t_class == $class && $t_method == $method)) {
                    return true;
                }
            }
            return false;
        };

        foreach ($filters as $name => $action) {
            if (is_string($action)) {
                $exclude = true;
            } else {
                list($action, $exclude) = $action;
            }

            if ($exclude && $isMatch($action, $route['controller'])) {
                $filter_list[] = $name;
            } elseif (!$exclude && !$isMatch($action, $route['controller'])) {
                $filter_list[] = $name;
            }
        }

        return $filter_list;
    }



    /**
     * 分析对象的方法中的参数
     * @param  mixed  $class   对象|对象名字
     * @param  string $method  方法名
     * @return array
     */
    private function _parseParams($class, $method)
    {
        // 解析参数
        if (version_compare(PHP_VERSION, '7.0', '>=')) {
            $params = $this->php7($class, $method);
        } else {
            $params = $this->php5($class, $method);
        }

        // 产生参数 实例|值
        $args = [];
        foreach ($params as $param) {
            list($optional, $type, $name, $default) = $param;

            // 1.是否是对象,如果是对象就实例化对象
            if ($this->getType($type) === 1) {
                $args[] = Container::get($type);
                continue;
            }

            // 2.传入参数
            $val = Request::val($name);
            if ($optional) {
                if ($val === null) {
                    throw new \Exception("need a parameter $name "); 
                } else {
                    $args[] = $val;
                }
            } else {
                $args[] = ($val === null) ? $default : $val;
            }
        }

        return $args;
    }

    
    /**
     * 主观分析字符串所代表的类型
     * @param  string $name 类型名称
     * @return integer      0:标量 1:object
     */
    private function getType($name)
    {
        if (empty($name)) {
            return 0;
        }
        $name   = strtolower($name);
        $scalar = ['integer' => 1, 'float' => 1, 'string' => 1, 'boolean' => 1];
        if ($scalar[$name]) {
            return 0;
        }
        return 1;
    }


    /**
     * 获取调用参数信息 5 及以下使用
     * optional,type,name,value
     * @param  [type] $obj    [description]
     * @param  [type] $method [description]
     * @return [type]         [description]
     */
    private function php5($obj, $method)
    {
        $params  = [];
        $subject = \ReflectionMethod::export($obj, $method, true);
        $subject = explode(PHP_EOL, $subject);
        $pattern = '/Parameter #\d* \[ <(.*)>(.*)\$([_a-zA-Z0-9]*) =?(.*)\]/';
        foreach ($subject as $line) {
            $line = trim($line);
            if (empty($line) || strpos($line, 'Parameter #') === false) {
                continue;
            }
            $num = preg_match_all($pattern, $line, $matches);
            if ($num > 0) {
                array_shift($matches);
                foreach ($matches as $key => &$val) {
                    if ($key == 0)
                        $val = ($val[0] == 'required');
                    else 
                        $val = trim($val[0], '" \'');
                }
                $params[] = $matches;
            }
        }
        unset($matches);
        return $params;
    }


    /**
     * 获取调用参数信息 7.0 以上有API可用
     * @param  [type] $obj    [description]
     * @param  [type] $method [description]
     * @return [type]         [description]
     */
    private function php7($obj, $method)
    {
        $params = [];
        $method = new \ReflectionMethod($obj, $method);
        $ref_params = $method->getParameters();
        foreach ($ref_params as $param) {
            $optional = $param->allowsNull();
            $type     = $param->getType();
            $name     = $param->getName();
            $default  = $param->getDefaultValue();
            $params[] = [$optional, $type, $name, $default];
        }
        return $params;
    }


    /**
     * 导入文件
     * @param  string $file  文件名称
     * @return void
     */
    private function import($file)
    {
        if (strpos('.', $file)) {
            $file = str_replace('.', '/', $file);
        }
        require_once APP_PATH.DS.$file;
    }


    public function __get($name)
    {
        if (property_exists(this, $name)) {
            return $this->{$name};
        }
    }


    public function __call($method, $arguments)
    {
        if (method_exists($this, $method)) {
            return call_user_func_array([$this, $method], $arguments);
        }
    }

    
    public static function __callStatic($method, $arguments)
    {
        $obj = static::singleton();
        if (method_exists($obj, $method)) {
            return call_user_func_array([$obj, $method], $arguments);
        }
    }


    public static function singleton()
    {
        if (static::$obj == null) {
            static::$obj = new static();
        }
        return static::$obj;
    }

}
